/**
 * 
 */
package adnotatio.server.xml.dom;

import java.util.Iterator;

import org.w3c.dom.Attr;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import adnotatio.common.xml.XMLDocument;
import adnotatio.common.xml.XMLElement;
import adnotatio.common.xml.XMLName;
import adnotatio.common.xml.XMLNode;

/**
 * @author kotelnikov
 */
public class DomXmlElement extends XMLElement {

    /**
     * Initializes this object with the real XML node object
     * 
     * @param document the document owning this node
     * @param node the node to set
     */
    public DomXmlElement(XMLDocument doc, Element e) {
        super(doc, e);
    }

    /**
     * @see adnotatio.common.xml.XMLElement#appendChild(java.lang.Object,
     *      java.lang.Object)
     */
    protected void appendChild(Object element, Object node) {
        ((Element) element).appendChild((Node) node);
    }

    /**
     * @see adnotatio.common.xml.XMLElement#getAttributeName(java.lang.Object)
     */
    protected XMLName getAttributeName(Object attr) {
        return getName((Attr) attr);
    }

    /**
     * @see adnotatio.common.xml.XMLElement#getAttributes(java.lang.Object)
     */
    protected Iterator getAttributes(Object node) {
        Element e = (Element) node;
        final NamedNodeMap map = e.getAttributes();
        return new Iterator() {
            int fPos;

            public boolean hasNext() {
                return fPos < map.getLength();
            }

            public Object next() {
                if (!hasNext())
                    return null;
                return map.item(fPos++);
            }

            public void remove() {
                throw new RuntimeException("Not implemented");
            }

        };
    }

    /**
     * @see adnotatio.common.xml.XMLElement#getAttributeValue(java.lang.Object)
     */
    protected String getAttributeValue(Object attr) {
        return ((Attr) attr).getValue();
    }

    /**
     * @see adnotatio.common.xml.XMLElement#getChildrenIterator(java.lang.Object)
     */
    protected Iterator getChildrenIterator(Object node) {
        final Element e = (Element) node;
        return new Iterator() {

            Node fChild = e.getFirstChild();

            /**
             * @see java.util.Iterator#hasNext()
             */
            public boolean hasNext() {
                return fChild != null;
            }

            /**
             * @see java.util.Iterator#next()
             */
            public Object next() {
                Node prev = fChild;
                fChild = fChild != null ? fChild.getNextSibling() : null;
                return prev;
            }

            public void remove() {
                throw new RuntimeException("Not implemented");
            }

        };
    }

    /**
     * Returns the name object corresponding to the given XML node
     * 
     * @param node the XML node for which the corresponding {@link XMLName}
     *        should be returned
     * @return the name object corresponding to the given XML node
     */
    private XMLName getName(Node node) {
        String name = node.getNodeName();
        String prefix = node.getPrefix();
        String namespace = node.getNamespaceURI();
        if (prefix != null) {
            name = name.substring(prefix.length() + 1);
        }
        XMLName result = new XMLName(namespace, prefix, name);
        return result;
    }

    /**
     * @see adnotatio.common.xml.XMLElement#getNodeName(java.lang.Object)
     */
    protected XMLName getNodeName(Object node) {
        return getName((Node) node);
    }

    /**
     * Creates and returns a new XML node wrapper for the specified DOM node.
     * 
     * @param node the DOM node for which a wrapper should be created
     * @return a new node corresponding to the given DOM node
     */
    protected XMLNode newNode(Object node) {
        XMLNode result = null;
        XMLDocument doc = getDocument();
        if (node instanceof CharacterData) {
            result = new DomXmlText(doc, (CharacterData) node);
        } else if (node instanceof Element) {
            result = new DomXmlElement(doc, (Element) node);
        }
        return result;
    }

}